package org.zbclan.bf2.rcon.query;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.pivot.serialization.BinarySerializer;
import org.apache.pivot.serialization.Serializer;
import org.apache.pivot.web.Query.Method;
import org.apache.pivot.web.QueryException;
import org.apache.pivot.web.server.QueryServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.zbclan.bf2.rcon.BF2RconAppRunner;
import org.zbclan.bf2.rcon.Home;
import org.zbclan.bf2.rcon.Profile;
import org.zbclan.bf2.rcon.SessionVariables;
import org.zbclan.bf2.rcon.event.ListLocalProfilesEvent;
import org.zbclan.bf2.rcon.event.PushEvent;
import org.zbclan.bf2.rcon.model.AudioSettings;
import org.zbclan.bf2.rcon.model.LocalProfile;
import org.zbclan.bf2.rcon.model.VideoSettings;

public class LocalProfileServlet extends QueryServlet implements Runnable {

	private static final String URL = "https://romeo-foxtrot.com/svn/test";
	private static final String SVN_HOME = "/home";

	/**
	 * 
	 */
	private static final long serialVersionUID = -6019642623115994000L;

	private static final Logger logger = LoggerFactory
			.getLogger(LocalProfileServlet.class);

	private HttpSession session;

	private Home home;

	private List<LocalProfile> profiles;

	// private String baseUrl;

	@Override
	protected Serializer<?> createSerializer(Method method, Path path)
			throws QueryException {
		return new BinarySerializer();
	}

	// @Override
	// protected Object doGet(Path path) throws QueryException {
	//
	// String result;
	// if (session != null) {
	// result = session.getId();
	// } else {
	// result = "invalid session";
	// }
	// System.out.println(result);
	// // System.out.println(path);
	//
	// return result;
	// }

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws IOException, ServletException {

		session = req.getSession();

		// /*
		// * http://localhost:8080/bf2rcon
		// */
		// String requestURL = req.getRequestURL().toString();
		// baseUrl = requestURL.substring(0,
		// requestURL.indexOf(req.getServletPath()));

		@SuppressWarnings("unchecked")
		Enumeration<String> names = req.getParameterNames();
		while (names.hasMoreElements()) {
			String p = (String) names.nextElement();
			for (String v : req.getParameterValues(p)) {
				System.out.println(p + " = " + v);
			}
		}

		super.service(req, resp);

		return;
	}

	@Override
	protected URL doPost(Path path, Object value) throws QueryException {

		home = (Home) value;

		parseProfiles();

		// display();

		BF2RconAppRunner.SCHEDULED_EXECUTOR.execute(this);

		/*
		 * index: persist and invoke solr data import handler
		 */
		try {
			persist();
			// solrImport();
		} catch (Exception ex) {
			logger.error("failed to build index", ex);
			throw new QueryException(ex);
		}

		return null;
	}

	private void display() {

		BF2RconAppRunner appRunner = (BF2RconAppRunner) session
				.getAttribute(SessionVariables.APP_RUNNER);
		appRunner.fireEvent(new ListLocalProfilesEvent(profiles));
		appRunner.fireEvent(new PushEvent());

		return;
	}

	private void svnCheckin(Home home, SVNRepository repository,
			Map<String, byte[]> checkout, String platformId)
			throws SVNException {

		ISVNEditor editor = repository.getCommitEditor(
				"updated local profiles of "
						+ session.getAttribute(SessionVariables.USER), null);
		{
			editor.openRoot(-1);
			{
				String dir = SVN_HOME;
				if (checkout.containsKey(dir)) {
					editor.openDir(dir, -1);
				} else {
					editor.addDir(dir, null, -1);
				}
				for (int i = 0; i < profiles.size(); i++) {
					Profile rawProfile = home.getProfiles().get(i);
					dir = SVN_HOME + "/" + profiles.get(i).getGamespyNick();
					if (checkout.containsKey(dir)) {
						editor.openDir(dir, -1);
					} else {
						editor.addDir(dir, null, -1);
					}
					{
						dir += "/" + platformId;
						if (checkout.containsKey(dir)) {
							editor.openDir(dir, -1);
						} else {
							editor.addDir(dir, null, -1);
						}
						{
							svnPutFile("Audio.con", rawProfile.getAudio(),
									checkout, dir, editor);
							svnPutFile("Controls.con",
									rawProfile.getControls(), checkout, dir,
									editor);
							svnPutFile("General.con", rawProfile.getGeneral(),
									checkout, dir, editor);
							svnPutFile("Haptic.con", rawProfile.getHaptic(),
									checkout, dir, editor);
							svnPutFile("mapList.con", rawProfile.getMapList(),
									checkout, dir, editor);
							svnPutFile("Profile.con", rawProfile.getProfile(),
									checkout, dir, editor);
							svnPutFile("ServerSettings.con",
									rawProfile.getServerSettings(), checkout,
									dir, editor);
							svnPutFile("Video.con", rawProfile.getVideo(),
									checkout, dir, editor);
						}
						editor.closeDir();
					}
					editor.closeDir();
				}
				editor.closeDir();
			}
			editor.closeDir();
		}
		SVNCommitInfo commitInfo = editor.closeEdit();
		System.out.println("The directory was added: " + commitInfo);

		return;
	}

	private boolean svnDiff(Home home, Map<String, byte[]> checkout,
			String platformId) {

		boolean diff = false;
		String dir = SVN_HOME;
		if (!checkout.containsKey(dir)) {
			diff = true;
		} else {
			for (int i = 0; i < profiles.size(); i++) {
				Profile rawProfile = home.getProfiles().get(i);
				dir = SVN_HOME + "/" + profiles.get(i).getGamespyNick();
				if (!checkout.containsKey(dir)) {
					diff = true;
					break;
				}
				dir += "/" + platformId;
				if (!checkout.containsKey(dir)) {
					diff = true;
					break;
				}
				if (svnDiff(dir + "/Audio.con", rawProfile.getAudio(), checkout)
						|| svnDiff(dir + "/Controls.con",
								rawProfile.getControls(), checkout)
						|| svnDiff(dir + "/General.con",
								rawProfile.getGeneral(), checkout)
						|| svnDiff(dir + "/Haptic.con", rawProfile.getHaptic(),
								checkout)
						|| svnDiff(dir + "/mapList.con",
								rawProfile.getMapList(), checkout)
						|| svnDiff(dir + "/Profile.con",
								rawProfile.getProfile(), checkout)
						|| svnDiff(dir + "/ServerSettings.con",
								rawProfile.getServerSettings(), checkout)
						|| svnDiff(dir + "/Video.con", rawProfile.getVideo(),
								checkout)) {
					diff = true;
					break;
				}
			}
		}

		return diff;
	}

	private boolean svnDiff(String file, String newContent,
			Map<String, byte[]> checkout) {

		boolean result;

		if (checkout.containsKey(file)) {
			String oldContent = new String(checkout.get(file));
			result = !oldContent.equals(newContent);
		} else {
			result = true;
		}

		return result;
	}

	private Map<String, byte[]> svnCheckout(Home home, SVNRepository repository)
			throws SVNException {

		Map<String, byte[]> checkout = new HashMap<String, byte[]>();
		{
			String platformId = home.getPlatformId();
			String dir = SVN_HOME;
			SVNNodeKind nodeKind = repository.checkPath(dir, -1);
			if (nodeKind != SVNNodeKind.NONE) {
				checkout.put(dir, null);

				for (int i = 0; i < profiles.size(); i++) {
					dir = SVN_HOME + "/" + profiles.get(i).getGamespyNick();
					nodeKind = repository.checkPath(dir, -1);
					if (nodeKind != SVNNodeKind.NONE) {
						checkout.put(dir, null);

						dir += "/" + platformId;
						nodeKind = repository.checkPath(dir, -1);
						if (nodeKind != SVNNodeKind.NONE) {
							checkout.put(dir, null);

							svnUpdate(dir + "/Audio.con", checkout, repository);
							svnUpdate(dir + "/Controls.con", checkout,
									repository);
							svnUpdate(dir + "/General.con", checkout,
									repository);
							svnUpdate(dir + "/Haptic.con", checkout, repository);
							svnUpdate(dir + "/mapList.con", checkout,
									repository);
							svnUpdate(dir + "/Profile.con", checkout,
									repository);
							svnUpdate(dir + "/ServerSettings.con", checkout,
									repository);
							svnUpdate(dir + "/Video.con", checkout, repository);

						}
					}
				}
			}
		}

		return checkout;
	}

	private void svnUpdate(String file, Map<String, byte[]> map,
			SVNRepository repository) throws SVNException {

		SVNNodeKind nodeKind;
		nodeKind = repository.checkPath(file, -1);
		if (nodeKind != SVNNodeKind.NONE) {
			map.put(file, svnGetFile(file, repository));
		}

		return;
	}

	private byte[] svnGetFile(String filePath, SVNRepository repository)
			throws SVNException {

		SVNProperties fileProperties = new SVNProperties();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		repository.getFile(filePath, -1, fileProperties, baos);

		return baos.toByteArray();
	}

	private void svnPutFile(String file, String content,
			Map<String, byte[]> checkout, String dir, ISVNEditor editor)
			throws SVNException {

		String path = dir + "/" + file;
		if (checkout.containsKey(path)) {
			byte[] oldContentBytes = checkout.get(path);
			String oldContent = new String(oldContentBytes);
			if (!content.equals(oldContent)) {
				editor.openFile(path, -1);
				editor.applyTextDelta(path, null);
				String checksum = new SVNDeltaGenerator().sendDelta(path,
						new ByteArrayInputStream(oldContentBytes), 0,
						new ByteArrayInputStream(content.getBytes()), editor,
						true);
				editor.closeFile(path, checksum);
			}
		} else {
			editor.addFile(path, null, -1);
			editor.applyTextDelta(path, null);
			String checksum = new SVNDeltaGenerator().sendDelta(path,
					new ByteArrayInputStream(content.getBytes()), editor, true);
			editor.closeFile(path, checksum);
		}

		return;
	}

	private void parseProfiles() {

		profiles = new ArrayList<LocalProfile>();

		for (Profile rawProfile : home.getProfiles()) {
			String dirName = rawProfile.getDir();
			String profileCon = rawProfile.getProfile();

			LocalProfile p = new LocalProfile();
			p.setPlatformId(home.getPlatformId());
			p.setPlatformUser(home.getPlatformUser());
			p.setPlatformDir(dirName);

			p.setPlatformOSArch(home.getPlatformOSArch());
			p.setPlatformOSName(home.getPlatformOSName());
			p.setPlatformOSVersion(home.getPlatformOSVersion());

			Matcher m;

			m = LocalProfile.NAME_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setName(m.group(1));
			}

			m = LocalProfile.NICK_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setNick(m.group(1));
			}

			m = LocalProfile.GAMESPY_NICK_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setGamespyNick(m.group(1));
			}

			m = LocalProfile.EMAIL_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setEmail(m.group(1));
			}

			m = LocalProfile.PASSWORD_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setPassword(m.group(1));
			}

			m = LocalProfile.TOTAL_PLAYED_TIME_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setTotalPlayedTime(Float.parseFloat(m.group(1)));
			}

			m = LocalProfile.NUM_TIMES_LOGGED_IN_PATTERN.matcher(profileCon);
			if (m.find()) {
				p.setNumTimesLoggedIn(Integer.parseInt(m.group(1)));
			}

			profiles.add(p);
		}

		return;
	}

	// private void solrImport() throws IOException {
	//
	// HttpClient httpClient = new DefaultHttpClient();
	// try {
	// HttpGet httpGet = new HttpGet(baseUrl
	// + "/solr/dataimport?command=delta-import");
	// ResponseHandler<String> responseHandler = new BasicResponseHandler();
	// String responseBody = httpClient.execute(httpGet, responseHandler);
	// logger.trace(responseBody);
	// } finally {
	// httpClient.getConnectionManager().shutdown();
	// }
	//
	// return;
	// }

	private void persist() throws IllegalAccessException,
			InvocationTargetException {
		// /*
		// * merge con files and send through message queue for another process
		// to
		// * build index
		// */
		//
		// MessageQueueService mq = new MessageQueueServiceRabbitImpl("gentoo");
		//
		// for (Profile r : home.getProfiles()) {
		// /*
		// * TODO process general, maplist, controls and haptic
		// */
		// String message = r.getProfile() + r.getAudio() + r.getVideo()
		// + r.getServerSettings();
		// mq.send("bf2rcon-index", message);
		// }

		List<Profile> rawProfiles = home.getProfiles();
		for (int i = 0; i < profiles.size(); i++) {
			LocalProfile profile = profiles.get(i);
			profile.setTimestamp(new Date());

			/*
			 * TODO parse audio, video, ...
			 */
			Profile rawProfile = rawProfiles.get(i);
			profile.setAudioSettings(parseAudioSettings(rawProfile.getAudio()));
			profile.setVideoSettings(parseVideoSettings(rawProfile.getVideo()));

			LocalProfile p = BF2RconAppRunner.PERSISTENCE
					.findLocalProfileByGamespyNick(profile.getGamespyNick());
			if (p == null) {
				BF2RconAppRunner.PERSISTENCE.save(profile);
			} else {
				// BeanUtils.copyProperties(p, profile);
				p.setEmail(profile.getEmail());
				p.setName(profile.getName());
				p.setNick(profile.getNick());
				p.setNumTimesLoggedIn(profile.getNumTimesLoggedIn());
				p.setPassword(profile.getPassword());
				p.setPlatformDir(profile.getPlatformDir());
				p.setPlatformId(profile.getPlatformId());
				p.setPlatformOSArch(profile.getPlatformOSArch());
				p.setPlatformOSName(profile.getPlatformOSName());
				p.setPlatformOSVersion(profile.getPlatformOSVersion());
				p.setPlatformUser(profile.getPlatformUser());
				p.setTimestamp(profile.getTimestamp());
				p.setTotalPlayedTime(profile.getTotalPlayedTime());

				BeanUtils.copyProperties(p.getAudioSettings(),
						profile.getAudioSettings());
				BeanUtils.copyProperties(p.getVideoSettings(),
						profile.getVideoSettings());

				BF2RconAppRunner.PERSISTENCE.save(p);
			}
		}

		return;
	}

	private VideoSettings parseVideoSettings(String video) {

		VideoSettings v = new VideoSettings();

		Matcher m;

		m = VideoSettings.TERRAIN_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setTerrainQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.GEOMETRY_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setGeometryQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.LIGHTING_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setLightingQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.DYNAMIC_LIGHTING_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setDynamicLightingQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.DYNAMIC_SHADOWS_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setDynamicShadowsQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.EFFECTS_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setEffectsQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.TEXTURE_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setTextureQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.TEXTURE_FILTERING_QUALITY_PATTERN.matcher(video);
		if (m.find()) {
			v.setTextureFilteringQuality(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.RESOLUTION_PATTERN.matcher(video);
		if (m.find()) {
			v.setResolution(m.group(1));
		}

		m = VideoSettings.ANTIALIASING_PATTERN.matcher(video);
		if (m.find()) {
			v.setAntialiasing(m.group(1));
		}

		m = VideoSettings.VIEW_DISTANCE_SCALE_PATTERN.matcher(video);
		if (m.find()) {
			v.setViewDistanceScale(Integer.parseInt(m.group(1)));
		}

		m = VideoSettings.VIDEO_OPTION_SCHEME_PATTERN.matcher(video);
		if (m.find()) {
			v.setVideoOptionScheme(Integer.parseInt(m.group(1)));
		}

		return v;
	}

	private AudioSettings parseAudioSettings(String audio) {

		AudioSettings a = new AudioSettings();

		Matcher m;

		m = AudioSettings.VOIP_ENABLED_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipEnabled(Boolean.parseBoolean(m.group(1)));
		}

		m = AudioSettings.VOIP_PLAYBACK_VOLUME_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipPlaybackVolume(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.VOIP_CAPTURE_VOLUME_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipCaptureVolume(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.VOIP_CAPTURE_THRESHOLD_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipCaptureThreshold(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.VOIP_BOOST_ENABLED_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipBoostEnabled(Boolean.parseBoolean(m.group(1)));
		}

		m = AudioSettings.VOIP_USE_PUSH_TO_TALK_PATTERN.matcher(audio);
		if (m.find()) {
			a.setVoipUsePushToTalk(Boolean.parseBoolean(m.group(1)));
		}

		m = AudioSettings.PROVIDER_PATTERN.matcher(audio);
		if (m.find()) {
			a.setProvider(m.group(1));
		}

		m = AudioSettings.SOUND_QUALITY_PATTERN.matcher(audio);
		if (m.find()) {
			a.setSoundQuality(m.group(1));
		}

		m = AudioSettings.EFFECTS_VOLUME_PATTERN.matcher(audio);
		if (m.find()) {
			a.setEffectsVolume(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.MUSIC_VOLUME_PATTERN.matcher(audio);
		if (m.find()) {
			a.setMusicVolume(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.HELP_VOICE_VOLUME_PATTERN.matcher(audio);
		if (m.find()) {
			a.setHelpVoiceVolume(Float.parseFloat(m.group(1)));
		}

		m = AudioSettings.ENGLISH_ONLY_VOICES_PATTERN.matcher(audio);
		if (m.find()) {
			a.setEnglishOnlyVoices(Boolean.parseBoolean(m.group(1)));
		}

		m = AudioSettings.ENABLE_EAX_PATTERN.matcher(audio);
		if (m.find()) {
			a.setEnableEAX(Boolean.parseBoolean(m.group(1)));
		}

		return a;
	}

	@Override
	public void run() {

		/*
		 * commit to svn:trunk/home/<gamespyNick>/<platformId>/
		 */
		try {
			DAVRepositoryFactory.setup();

			final SVNRepository repository = SVNRepositoryFactory.create(SVNURL
					.parseURIDecoded(URL));

			String username = "fren";
			String password = "wyy118";
			ISVNAuthenticationManager authManager = SVNWCUtil
					.createDefaultAuthenticationManager(username, password);

			// set an auth manager which will provide user credentials
			repository.setAuthenticationManager(authManager);

			try {
				System.out.println("Repository Root: "
						+ repository.getRepositoryRoot(true));
				System.out.println("Repository UUID: "
						+ repository.getRepositoryUUID(true));

				long latestRevision = repository.getLatestRevision();
				System.out.println("Repository latest revision: "
						+ latestRevision);

				Map<String, byte[]> checkout = svnCheckout(home, repository);
				String platformId = home.getPlatformId();

				if (svnDiff(home, checkout, platformId)) {
					svnCheckin(home, repository, checkout, platformId);
				}
			} finally {
				repository.closeSession();
			}

		} catch (SVNException ex) {
			logger.error("failed to commit to svn", ex);
		}

		return;
	}
}
